#include "str.h"
#include "ansi.h"

/**
 * TODO: 字符串计算是可以在软件层面加速的，通常的字符操作都是按照字节进行，我们可以
 *       按照 CPU 位宽进行（例如 4 字节），这样做的前提是 elf 文件的节区都是 CPU 位宽对齐（例如 4 字节对齐），
 *       我想这应该是编译器的默认行为，但是需要确认的是单字节访存时编译器是否会优化为对齐访存。
 */

/**
 * NOTE: the following functions who use the type `mx_string_pt` in there parameters list means that
 * this function will modify the string, so that this input string can't be const. on the contraty, it is `mx_cstring_pt`
 */

/**
 * get the string length
 * @param s: target string
 * @return: string length
 */
mx_usize_t str_getlen (mx_cstring_pt s)
{
    mx_cstring_pt n = s;
    while (*n)
        n ++;
    return n - s;
}

/**
 * check whether the string is empty ("")
 * @param s: target string
 * @return: is empty or not
 */
mx_bool_t str_isempty (mx_cstring_pt s)
{
    return s[0] == '\0';
}

/**
 * check whether the string is integer
 * @param s: target string
 * @return: is integer or not
 */
mx_bool_t str_isinteger (mx_cstring_pt s)
{
    if (*s == '-' || *s == '+')
        s ++;
    if (*s == '\0')
        return mx_false;
    while (ANSI_ISNUMBER(*s))
        s ++;
    return *s == '\0';    
}

/**
 * check whether the specified two strings are equal
 * @param s1: string 1
 * @param s1: string 2
 * @return: is equal or not
 */
mx_bool_t str_isequal (mx_cstring_pt s1, mx_cstring_pt s2)
{
    while (*s1 && *s1 == *s2)
    {
        s1 ++;
        s2 ++;
    }
    return *s1 == *s2;
}

/**
 * copy string
 * @param dst: target storage area
 * @param src: the source string
 * @return: void
 */
void str_copy (mx_string_pt dst, mx_cstring_pt src)
{
    while (*dst = *src)
    {
        dst ++;
        src ++;
    }
}

/**
 * copy string within the specified size
 * @param dst: target storage area
 * @param src: the source string
 * @param dcnt: the count of `dst`
 * @return: void
 */
void str_copyn (mx_string_pt dst, mx_cstring_pt src, mx_usize_t dcnt)
{
    while (dcnt && (*dst = *src))
    {
        dcnt --;
        dst ++;
        src ++;
    }
    if (dcnt == 0)
        *dst = '\0';
}

/**
 * count the number of specified char in target string
 * @param src: the source string
 * @param c: the matching char
 * @return: the number of chars
 */
mx_usize_t str_cn (mx_cstring_pt src, char c)
{
    mx_usize_t cnt = 0;
    if (!c)
        return cnt;
    while ((*src == c && (++cnt)) || *src)
        src ++;
    return cnt;
}

/**
 * find the specified char in target string, forward lookup
 * @param src: the source string
 * @param c: the matching char
 * @return: the index of the char, -1 means not found
 */
mx_size_t str_ci (mx_cstring_pt src, char c)
{
    mx_cstring_pt next = src;
    do
    {
        if (*next == c)
            return next - src;
    } while (*next && ++next);
    return -1;
}

/**
 * find the specified char in target string, reverse lookup
 * @param src: the source string
 * @param c: the matching char
 * @return: the index of the char, -1 means not found
 */
mx_size_t str_cir (mx_cstring_pt src, char c)
{
    mx_size_t idx = -1;
    mx_cstring_pt next = src;
    while ((*next == c && (idx = next - src, c)) || *next)
        next ++;
    return idx;    
}

/**
 * count the number of specified string in target string
 * @param src: the source string
 * @param s: the matching string
 * @return: the number of strings
 */
mx_usize_t str_sn (mx_cstring_pt src, mx_cstring_pt s)
{
    if (!*s)
        return 0;
    mx_usize_t cnt = 0;
    mx_cstring_pt n = s;
    while (*src)
    {
        if (*n == *src)
            n ++;
        else if (!*n)
        {
            cnt ++;
            n = *s == *src ? s + 1 : s;
        }
        else
            n = s;
        src ++;
    }
    if (!*n)
        cnt ++;
    return cnt;
}

/**
 * find the specified string in target string, forward lookup
 * @param src: the source string
 * @param s: the matching string
 * @return: the index of the string, -1 means not found
 */
mx_size_t str_si (mx_cstring_pt src, mx_cstring_pt s)
{
    if (!*s)
        return -1;
    mx_cstring_pt r = src, n = s;
    while (*r)
    {
        if (*n == *r)
            n ++;
        else if (!*n)
            return r - src - (n - s);
        else
            n = s;
        r ++;
    }
    if (!*n)
        return r - src - (n - s);
    return -1;
}

/**
 * find the specified string in target string, reverse lookup
 * @param src: the source string
 * @param s: the matching string
 * @return: the index of the string, -1 means not found
 */
mx_size_t str_sir (mx_cstring_pt src, mx_cstring_pt s)
{
    if (!*s)
        return -1;
    mx_usize_t idx = -1;
    mx_cstring_pt r = src, n = s;
    while (*r)
    {
        if (*n == *r)
            n ++;
        else if (!*n)
        {
            idx = r - src - (n - s);
            n = *s == *r ? s + 1 : s;
        }
        else
            n = s;
        r ++;
    }
    if (!*n)
        return r - src - (n - s);
    return idx;
}

/**
 * delete the before and after spaces in target string
 * @param s: target string
 * @return: the trimed string
 */
mx_string_pt str_trim (mx_string_pt s)
{
    mx_string_pt beg, end;
    while (*s == ' ')
        s ++;
    if (*s)
        beg = s++;
    else
        return s;
    end = beg;
    while (*s)
    {
        if (*s != ' ')
            end = s;
        s ++;
    }
    *(end + 1) = '\0';
    return beg;
}

/**
 * split the string using the specified char
 * @param src: the source string, can't be const
 * @param c: the char used for split
 * @param arr: the array obtained agter split
 * @param arrlen: the array length, if it is 0 then the string will not be split
 * @return: the actual number of split
 */
mx_usize_t str_splitc (mx_string_pt src, char c, mx_string_pt* arr, mx_usize_t arrlen)
{
    if (arrlen == 0)
    {
        mx_usize_t cnt = 1;
        while ((*src == c && ++cnt) || *src)
            src ++;
        return cnt;
    }
    else
    {
        mx_usize_t cnt = 0;
        arr[cnt] = src;
        arrlen --;
        while (*src)
        {
            if (*src == c)
            {
                cnt ++;
                *src = '\0';
                if (arrlen)
                {
                    arr[cnt] = ++src;
                    arrlen --;
                    continue;
                }
            }
            src ++;
        }
        return cnt + 1;
    }
}

/**
 * split the string using the specified char and trim each string
 * @param src: the source string, can't be const
 * @param c: the char used for split
 * @param arr: the array obtained agter split
 * @param arrlen: the array length, if it is 0 then the string will not be split
 * @return: the actual number of split
 */
mx_usize_t str_splitc_trim (mx_string_pt src, char c, mx_string_pt* arr, mx_usize_t arrlen)
{
    mx_usize_t cnt = 0;
    if (arrlen == 0)
    {
        mx_bool_t exist = mx_false;
        while (*src)
        {
            if (*src == c)
            {
                if (exist)
                {
                    cnt ++;
                    exist = mx_false;
                }
            }
            else
                exist = mx_true;
            src ++;
        }
        return exist ? cnt + 1 : cnt;
    }
    else
    {
        mx_string_pt beg = mx_null, end;
        while (*src)
        {
            if (*src == c)
            {
                if (beg)
                {
                    *(end + 1) = '\0';
                    if (arrlen)
                    {
                        arr[cnt] = beg;
                        arrlen --;
                    }
                    beg = mx_null;
                    cnt ++;
                }
            }
            else
            {
                if (*src != ' ')
                {
                    if (!beg)
                        beg = src;
                    end = src;
                }
            }
            src ++;
        }
        if (beg)
        {
            *(end + 1) = '\0';
            if (arrlen)
                arr[cnt] = beg;
            cnt ++;
        }
        return cnt;
    }
}

/**
 * repalce char(s) in the string to another char
 * @param src: the source string, can't be const
 * @param f: from
 * @param t: to
 * @return: void
 */
void str_replacec (mx_string_pt src, char f, char t)
{
    if (f)
    {
        while ((*src == f && (*src = t)) || *src)
            src ++;
    }
}

/**
 * convent char(s) to upcase in source string
 * @param src: the source string, can't be const
 * @return: void
 */
void str_upcase (mx_string_pt src)
{
    while (*src)
    {
        if (ANSI_ISLOWCASE(*src))
            *src -= 0x20;
        src ++;
    }
}

/**
 * convent char(s) to lowcase in source string
 * @param src: the source string, can't be const
 * @return: void
 */
void str_lowcase (mx_string_pt src)
{
    while (*src)
    {
        if (ANSI_ISUPCASE(*src))
            *src += 0x20;
        src ++;
    }
}
